﻿using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.PythonHelp.CompleteKeywords;
using MahApps.Metro.Controls;
using Microsoft.Win32;
using Org.BouncyCastle.Tls.Crypto;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// PythonEditor.xaml 的交互逻辑
    /// </summary>
    public partial class PythonEditor : MetroWindow
    {
        /// <summary>
        /// 自定义高亮定义，完整定义。
        /// 但一千多行的帮助文档就明显延迟。
        /// </summary>
        private static IHighlightingDefinition pythonHighlighting;

        private static Converter.ModifiedToText modifiedToTextConverter = new LunarMarkdownEditor.Converter.ModifiedToText();
        /// <summary>
        /// [只读]bool to "已修改 未修改" 的字符串的转换器。
        /// </summary>
        public static Converter.ModifiedToText ModifiedToTextConverter { get { return modifiedToTextConverter; } }

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static PythonEditor()
        {
            Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.Python.xshd");
            if (s != null)
            {
                using (XmlReader reader = new XmlTextReader(s))
                {
                    pythonHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.
                        HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }

        private IPythonScriptItem ownerItem;

        public IPythonScriptItem OwnerItem { get { return this.ownerItem; } }

        /// <summary>
        /// [构造方法]
        /// </summary>
        public PythonEditor(string fullPath, string titleText, IPythonScriptItem ownerItem)
        {
            InitializeComponent();

            this.fullPath = fullPath;
            this.ownerItem = ownerItem;
            this.editorBase.MasterEditor = this;

            if (ownerItem != null)
            {
                switch (ownerItem.Location)
                {
                    case PythonScriptLocation.Workspace:
                        tbLocation.Text = "【工作区脚本】";
                        break;
                    //case PythonScriptLocation.Global:
                    default:
                        tbLocation.Text = "【全局脚本】";
                        break;
                }
            }

            isLmePyScript = new FileInfo(fullPath).Name.StartsWith("Lme_");
            var preTitle = "";
            if (isLmePyScript)
            {
                btnOK.IsEnabled = false;
                btnSaveAndRun.IsEnabled = false;
                btnSaveAndClose.IsEnabled = false;
                preTitle = "[内置|只读]";

                miCut.IsEnabled = miCutMain.IsEnabled =
                    miUndo.IsEnabled = miUndoMain.IsEnabled =
                    miRedo.IsEnabled = miRedoMain.IsEnabled =
                    miPaste.IsEnabled = miPasteMain.IsEnabled =
                    miReplaceMain.IsEnabled = false;

                editorBase.IsReadOnly = true;
                tbModifiedinfo.Text = "内置脚本，不能修改！可复制到其它脚本文件中修改使用。";
                tbModifiedinfo.TextWrapping = TextWrapping.Wrap;
            }

            if (string.IsNullOrEmpty(titleText))
            {
                this.Title = /*Globals.AppName +*/ preTitle + " - Python 脚本编辑器";
            }
            else this.Title = /*Globals.AppName + " - " +*/ preTitle + titleText;

            // editorBase.Options.ShowSpaces = false;
            // editorBase.Options.ShowTabs = true;
            // editorBase.Options.ConvertTabsToSpaces = false;
            // editorBase.Options.AutoAlignTabs = false;
            // editorBase.Options.HighlightCurrentLine = true;
            // editorBase.Options.ShowEndOfLine = false;
            // editorBase.ShowLineNumbers = true;
            // editorBase.WordWrap = false;

            HighlightingManager.Instance.RegisterHighlighting("Python Highlighting", new string[] { ".py" }, pythonHighlighting);
            editorBase.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".py");

            // 初始化参数信息框高亮
            Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("LunarSF.SHomeWorkshop.LunarMarkdownEditor.ArgsInfo.SyntaxHightlightRulesets.xshd");
            if (s != null)
            {
                using (XmlReader reader = new XmlTextReader(s))
                {
                    var argsInfoHightLight = ICSharpCode.AvalonEdit.Highlighting.Xshd.HighlightingLoader.Load(reader, ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance);

                    ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.RegisterHighlighting("Args Info Highlighting", new string[] { ".arg" }, argsInfoHightLight);
                    ateArgsInfo.SyntaxHighlighting = ICSharpCode.AvalonEdit.Highlighting.HighlightingManager.Instance.GetDefinitionByExtension(".arg");
                }
            }
            // 设置参数信息框相关属性
            ateArgsInfo.SnapsToDevicePixels = true;//避免折叠线模糊
            ateArgsInfo.Background = Brushes.White;

            ateArgsInfo.ShowLineNumbers = false;
            ateArgsInfo.Options.ShowColumnRuler = false;
            ateArgsInfo.Options.ShowSpaces = false;
            // AvalonEdit本来使用间隔号代替空格，但间隔号是全角字符，要占据两个半角的宽度；
            // 我把它改为使用加重号，这样就只需要占据一个半角宽度了。
            ateArgsInfo.Options.ShowTabs = false;
            ateArgsInfo.Options.AutoAlignTabs = false;//将Tab显示为“--+”，并禁止Tab自动伸缩尺寸。

            TextOptions.SetTextFormattingMode(ateArgsInfo, TextFormattingMode.Display);

            ateArgsInfo.Options.ShowBoxForControlCharacters = false;
            ateArgsInfo.Options.ShowEndOfLine = false;
            ateArgsInfo.Options.AllowScrollBelowDocument = true;
            ateArgsInfo.Options.HighlightCurrentLine = false;
            ateArgsInfo.Options.ShowColumnRuler = false;

            ateArgsInfo.TextArea.SelectionBorder = new Pen(Brushes.Transparent, 1);
            ateArgsInfo.TextArea.SelectionBrush = new SolidColorBrush(Color.FromArgb(255, 145, 83, 0));  // Color.FromArgb(255, 240, 106, 0) 是那两条行高亮线色
            ateArgsInfo.TextArea.SelectionCornerRadius = 0;

            ateArgsInfo.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.DefaultIndentationStrategy();
            ateArgsInfo.WordWrap = true;

            // 如果文件存在，载入之
            if (File.Exists(fullPath))
            {
                var text = File.ReadAllText(fullPath);
                var importIndex = text.IndexOf("# <<<Auto-Import-Code-Lines>>>");
                if (importIndex >= 0)
                {
                    this.editorBase.Text = text.Substring(importIndex + "# <<<Auto-Import-Code-Lines>>>".Length).TrimStart(new char[] { '\r', '\n', ' ', '\t', });
                }
                else
                {
                    this.editorBase.Text = text;
                }

                //this.editorBase.Load(fullPath);
                this.editorBase.IsModified = false;
            }

            findAndReplaceDialog = new FindReplaceDialog(editorBase);

            editorBase.EnableAutoCompleteChanged += EditorBase_EnableAutoCompleteChanged;
            this.PreviewTextInput += PythonEditor_PreviewTextInput;
        }

        public PythonEditBase EditorBase { get => this.editorBase; }

        public void EditorBase_EnableAutoCompleteChanged(object sender, EnableAutoCompleteEventArgs e)
        {
            if (e.NewValue)
            {
                tbEnableAutoComplete.Text = "【自动提示】";
                spEnableAutoComplete.Background = Brushes.White;
                tbEnableAutoComplete.Foreground = dpBottom.Background;
                miEnableAutoComplete.IsChecked = true;
            }
            else
            {
                tbEnableAutoComplete.Text = "【禁用提示】";
                spEnableAutoComplete.Background = Brushes.Transparent;
                tbEnableAutoComplete.Foreground = Brushes.White;
                miEnableAutoComplete.IsChecked = false;
            }
        }

        public IPythonScriptItem MasterItem { get; set; } = null;

        private bool isLmePyScript = false;

        private string fullPath = "";
        /// <summary>
        /// [只读]Python 脚本文件的磁盘路径。
        /// </summary>
        public string FullPath { get { return this.fullPath; } }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            SavePythonScriptFile();
        }

        internal bool SavePythonScriptFile()
        {
            if (isLmePyScript)
            {
                LMessageBox.Show("此脚本是 LME 内置脚本，不能保存。" +
                    "请另外创建一个脚本文件，并将当前脚本复制到新创建的脚本文件中使用。\r\n\r\n" +
                    "★说明：不允许修改是因为这些内置脚本在安装 LME 的新版本后可能会更新——这会覆盖用户对脚本的修改成果。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }

            try
            {
                if (editorBase.IsModified == false) return true;

                using (StreamWriter sw = new StreamWriter(this.fullPath))
                {
                    if (Utils.R.匹配(this.editorBase.Text, @"(?<=(^[;；].*))<{3,}([ 　\t]*auto[ 　\t]*import[ 　\t]*)|([ 　\t]*自动引入[ 　\t]*)|([ 　\t]*自动导入[ 　\t]*)>{3,}(?=(.*$))", true, true))
                    {
                        var defaultImportCodeLines = @"

#-*- coding: UTF-8 -*- 此行指定字符编码，勿删！

import sys 
import clr
clr.AddReference(""PresentationFramework"")
clr.AddReference(""PresentationCore"")
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Media import *
from System.Text.RegularExpressions import *

clr.AddReference(""LunarMarkdownEditor"")
from LunarSF.SHomeWorkshop.LunarMarkdownEditor import Globals, LButton, LMenuItem, LTopMenuItem, LWindow
from LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils import R, Text, TextPiece, QRBuilder
from LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp import *

clr.AddReference(""MahApps.Metro"")
import MahApps.Metro.Controls


# <<<Auto-Import-Code-Lines>>>
";

                        sw.Write(defaultImportCodeLines + this.editorBase.Text);
                    }
                    else
                    {
                        sw.Write(this.editorBase.Text);
                    }

                    editorBase.IsModified = false;
                }

                //保存之后，要刷新对应列表项上的标题/描述/调用方式等三个选项。
                if (ownerItem != null)
                {
                    var oldGroupName = ownerItem.GroupName;
                    ownerItem.LastWriteTime = new FileInfo(this.fullPath).LastWriteTime;

                    string title = "Python 脚本";
                    string description = "<无描述文本>";
                    string callMode = "0";
                    string foregroundText = "Black";
                    string backgroundText = "Transparent";
                    string groupName = "";
                    string shortCutText = "";
                    string toolBarButtonText = "";
                    string popupButtonGroupsText = "";
                    Brush foreground, background;
                    // bool autoImport = false;

                    int index = 1;
                    var line = editorBase.Document.GetLineByNumber(index);
                    while (line != null && index <= 80)
                    {
                        var lineText = editorBase.Document.GetText(line.Offset, line.Length);
                        Globals.MainWindow.TryReadPythonScriptTitle(lineText, ref title);
                        Globals.MainWindow.TryReadPythonScriptDescription(lineText, ref description);
                        Globals.MainWindow.TryReadPythonScriptCallMode(lineText, ref callMode);
                        Globals.MainWindow.TryReadPythonScriptItemForeground(lineText, ref foregroundText);
                        Globals.MainWindow.TryReadPythonScriptItemBackground(lineText, ref backgroundText);
                        Globals.MainWindow.TryReadPythonScriptItemGroupName(lineText, ref groupName);
                        Globals.MainWindow.TryReadPythonScriptShortCut(lineText, ref shortCutText);
                        Globals.MainWindow.TryReadPythonScriptToolBarButtonText(lineText, ref toolBarButtonText);
                        Globals.MainWindow.TryReadPythonScriptPopupButtonGroupsText(lineText, ref popupButtonGroupsText);
                        // autoImport = Globals.MainWindow.TryReadPythonScriptAutoImportCodeLines(lineText);
                        // 用正则太麻烦了

                        if (string.IsNullOrWhiteSpace(lineText) == false) index++;

                        line = line.NextLine;
                    }

                    if (string.IsNullOrWhiteSpace(title)) title = "未设置标题";
                    if (string.IsNullOrWhiteSpace(description)) description = "<无描述文本>";
                    if (string.IsNullOrWhiteSpace(callMode)) callMode = "0";

                    foreground = BrushManager.GetBrush(foregroundText);
                    if (foreground == null) foreground = BrushManager.GetBrushByChineseName(foregroundText);

                    background = BrushManager.GetBrush(backgroundText);
                    if (background == null) background = BrushManager.GetBrushByChineseName(backgroundText);

                    if (string.IsNullOrEmpty(title))
                    {
                        this.Title = Globals.AppName + " - Python 脚本编辑器";
                    }
                    else this.Title = Globals.AppName + " - " + title;

                    ownerItem.Title = title;
                    ownerItem.TbDescription.Text = description;
                    ownerItem.HeaderForeground = foreground;
                    ownerItem.HeaderBackground = background;
                    ownerItem.GroupName = groupName;
                    ownerItem.RefreshGroupName();
                    ownerItem.RefreshCallMode(ownerItem.ConvertToPythonScriptCallMode(callMode));
                    ownerItem.ToolBarButtonText = toolBarButtonText;
                    ownerItem.PopupButtonGroupsText = popupButtonGroupsText;

                    ownerItem.ShortCutText = shortCutText;
                    ownerItem.RefreshShortCut();

                    if (string.IsNullOrWhiteSpace(toolBarButtonText) == false)
                    {
                        Button btn = null;
                        PythonScriptMenuItem mi = null;
                        switch (OwnerItem.Location)
                        {
                            case PythonScriptLocation.Workspace:
                                {
                                    foreach (var ue in Globals.MainWindow.workspacePyScriptsToolBar.Items)
                                    {
                                        var tbtn = ue as Button;
                                        if (tbtn == null || tbtn.Tag == null) continue;
                                        var psli = tbtn.Tag as PythonScriptListItem;
                                        if (psli == null || psli.FullPath.ToLower() != ownerItem.FullPath.ToLower()) continue;

                                        btn = tbtn;
                                    }
                                    foreach (var ue in Globals.MainWindow.cmWorkspacePythonScripts.Items)
                                    {
                                        var cmi = ue as PythonScriptMenuItem;
                                        if (cmi == null || cmi.Tag == null) continue;
                                        var psli = cmi.Tag as PythonScriptListItem;
                                        if (psli == null || psli.FullPath.ToLower() != ownerItem.FullPath.ToLower()) continue;

                                        mi = cmi;
                                    }
                                    break;
                                }
                            default:
                                {
                                    foreach (var ue in Globals.MainWindow.pyScriptsToolBar.Items)
                                    {
                                        var tbtn = ue as Button;
                                        if (tbtn == null || tbtn.Tag == null) continue;
                                        var psli = tbtn.Tag as PythonScriptListItem;
                                        if (psli == null || psli.FullPath.ToLower() != ownerItem.FullPath.ToLower()) continue;

                                        btn = tbtn;
                                    }
                                    foreach (var ue in Globals.MainWindow.cmGlobalsPythonScripts.Items)
                                    {
                                        var cmi = ue as PythonScriptMenuItem;
                                        if (cmi == null || cmi.Tag == null) continue;
                                        var psli = cmi.Tag as PythonScriptListItem;
                                        if (psli == null || psli.FullPath.ToLower() != ownerItem.FullPath.ToLower()) continue;

                                        mi = cmi;
                                    }
                                    break;
                                }
                        }

                        if (btn == null)
                        {
                            btn = new Button()
                            {
                                FontSize = 14,
                                Content = ownerItem.ToolBarButtonText,
                                Style = TryFindResource("LSquareButtonStyle") as Style,
                                BorderThickness = new Thickness(1, 1, 1, 1),
                                Margin = new Thickness(2, 2, 2, 0),
                                Height = 20,
                                Padding = new Thickness(4, 0, 4, 0),
                                FontWeight = FontWeights.Normal,
                                Tag = ownerItem,
                            };

                            if (string.IsNullOrWhiteSpace(OwnerItem.ShortCutText))
                            {
                                btn.ToolTip = ownerItem.Title;
                            }
                            else
                            {
                                btn.ToolTip = "【" + ownerItem.Title + "】";
                            }

                            var btnContextMenu = new ContextMenu();
                            var miOpenScript = new MenuItem()
                            {
                                Header = $"编辑 {ownerItem.Title}(_E)",
                                Style = TryFindResource("MetroMenuItem") as Style,
                                Tag = ownerItem,
                            };
                            miOpenScript.Click += MiOpenScript_Click;
                            btnContextMenu.Items.Add(miOpenScript);
                            btn.ContextMenu = btnContextMenu;
                            btn.MouseRightButtonDown += Btn_MouseRightButtonDown;

                            if (string.IsNullOrWhiteSpace(ownerItem.ShortCutText) == false && ownerItem.ShortCutText != "无")
                            {
                                btn.ToolTip = ownerItem.Title + "\r\n" + ownerItem.ShortCutText;
                            }
                            else
                            {
                                btn.ToolTip = ownerItem.Title;
                            }

                            btn.Click += Btn_Click;

                            switch (OwnerItem.Location)
                            {
                                case PythonScriptLocation.Workspace:
                                    {
                                        Globals.MainWindow.workspacePyScriptsToolBar.Items.Add(btn);
                                        Globals.MainWindow.cmWorkspacePythonScripts.Items.Add(new PythonScriptMenuItem(btn));
                                        if (Globals.MainWindow.workspacePyScriptsToolBar.Items.Count > 0)
                                        {
                                            Globals.MainWindow.workspacePyScriptsToolBar.Visibility = Visibility.Visible;
                                            Globals.MainWindow.cmWorkspacePythonScripts.Visibility =
                                                Globals.MainWindow.mWorkspacePythonScripts.Visibility = Visibility.Visible;
                                        }
                                        else
                                        {
                                            Globals.MainWindow.workspacePyScriptsToolBar.Visibility = Visibility.Collapsed;
                                            Globals.MainWindow.cmWorkspacePythonScripts.Visibility =
                                                Globals.MainWindow.mWorkspacePythonScripts.Visibility = Visibility.Collapsed; ;
                                        }
                                        break;
                                    }
                                // case PythonScriptLocation.Global:
                                default:
                                    {
                                        Globals.MainWindow.pyScriptsToolBar.Items.Add(btn);
                                        if (Globals.MainWindow.pyScriptsToolBar.Items.Count > 0)
                                        {
                                            Globals.MainWindow.pyScriptsToolBar.Visibility = Visibility.Visible;
                                            Globals.MainWindow.cmGlobalsPythonScripts.Visibility =
                                                Globals.MainWindow.mGlobalPythonScripts.Visibility = Visibility.Visible;
                                        }
                                        else
                                        {
                                            Globals.MainWindow.pyScriptsToolBar.Visibility = Visibility.Collapsed;
                                            Globals.MainWindow.cmGlobalsPythonScripts.Visibility =
                                                Globals.MainWindow.mGlobalPythonScripts.Visibility = Visibility.Collapsed;
                                        }
                                        break;
                                    }
                            }

                            if (Globals.MainWindow.PerspectiveMode == Perspective.MiniMode)
                            {
                                if (Globals.MainWindow.workspacePyScriptsToolBar.Items.Count <= 0 &&
                                    Globals.MainWindow.pyScriptsToolBar.Items.Count <= 0)
                                {
                                    Globals.MainWindow.mainToolBarTray.Visibility = Visibility.Collapsed;
                                    Globals.MainWindow.rdTopToolbarArea.Height = new GridLength(0);
                                }
                                else
                                {
                                    Globals.MainWindow.rdTopToolbarArea.Height = new GridLength(0, GridUnitType.Auto);
                                    Globals.MainWindow.mainToolBarTray.Visibility = Visibility.Visible;
                                }
                            }
                            else
                            {
                                Globals.MainWindow.mainToolBarTray.Visibility = Visibility.Visible;
                            }
                        }
                        else
                        {
                            //if (string.IsNullOrWhiteSpace(ownerItem.ToolBarButtonText)) { }   // 肯定不是，前面已经判断过了
                            //else
                            //{
                            mi.Header =  // btn 非 null，则 mi 必非 null！
                                btn.Content = ownerItem.ToolBarButtonText;

                            if (string.IsNullOrWhiteSpace(ownerItem.ShortCutText) == false && ownerItem.ShortCutText != "无")
                            {
                                btn.ToolTip = $"【{ownerItem.ShortCutText.ToUpper()}】{ownerItem.Title}";
                            }
                            else
                            {
                                btn.ToolTip = ownerItem.Title;
                            }
                            //}
                        }
                    }
                    else
                    {
                        switch (ownerItem.Location)
                        {
                            case PythonScriptLocation.Workspace:
                                {
                                    Button btn = null;
                                    foreach (var ue in Globals.MainWindow.workspacePyScriptsToolBar.Items)
                                    {
                                        var tbtn = ue as Button;
                                        if (tbtn == null || tbtn.Tag == null) continue;
                                        var psli = tbtn.Tag as PythonScriptListItem;
                                        if (psli == null || psli != ownerItem) continue;

                                        btn = tbtn;
                                        break;
                                    }

                                    if (btn != null)
                                    {
                                        Globals.MainWindow.workspacePyScriptsToolBar.Items.Remove(btn);
                                        if (Globals.MainWindow.workspacePyScriptsToolBar.Items.Count <= 0)
                                        {
                                            Globals.MainWindow.workspacePyScriptsToolBar.Visibility = Visibility.Collapsed;
                                        }
                                    }
                                    break;
                                }
                            //case PythonScriptLocation.Global:
                            default:
                                {
                                    Button btn = null;
                                    foreach (var ue in Globals.MainWindow.pyScriptsToolBar.Items)
                                    {
                                        var tbtn = ue as Button;
                                        if (tbtn == null || tbtn.Tag == null) continue;
                                        var psli = tbtn.Tag as PythonScriptListItem;
                                        if (psli == null || psli != ownerItem) continue;

                                        btn = tbtn;
                                        break;
                                    }

                                    if (btn != null)
                                    {
                                        Globals.MainWindow.pyScriptsToolBar.Items.Remove(btn);
                                        if (Globals.MainWindow.pyScriptsToolBar.Items.Count <= 0)
                                        {
                                            Globals.MainWindow.pyScriptsToolBar.Visibility = Visibility.Collapsed;
                                        }
                                    }
                                    break;
                                }
                        }
                    }

                    if (oldGroupName != groupName)
                    {
                        var result = LMessageBox.Show("您为当前脚本设置了新的分组名称，需要立即更新脚本在列表中的位置吗？\r\n\r\n　　如果选择【否】，将在下一次启动时自动更新。", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                        if (result == MessageBoxResult.Yes)
                        {
                            if (this.ownerItem.Location == PythonScriptLocation.Workspace)
                            {
                                Globals.MainWindow.LoadWorkspacePythonScriptFiles();
                            }
                            else
                            {
                                Globals.MainWindow.LoadPythonScriptFiles();
                            }
                        }
                    }

                    foreach (var ue in Globals.MainWindow.mainTabControl.Items)
                    {
                        var edit = ue as MarkdownEditor;
                        if (edit == null || edit.EditorBase.PopupToolBar == null) continue;

                        edit.EditorBase.PopupToolBar.LoadPythonScriptButtonGroups();
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        private void Btn_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            var btn = sender as Button;
            if (btn == null || btn.ContextMenu == null) return;

            btn.ContextMenu.IsOpen = true;
        }

        private void MiOpenScript_Click(object sender, RoutedEventArgs e)
        {
            var mi = sender as MenuItem;
            if (mi == null) return;

            var ownerItem = mi.Tag as PythonScriptListItem;
            if (ownerItem == null) return;

            ownerItem.EditScriptFile();
        }

        private void Btn_Click(object sender, RoutedEventArgs e)
        {
            var btn = sender as Button;
            if (btn == null) return;

            var psli = btn.Tag as PythonScriptListItem;
            if (psli == null) return;

            psli.RunScript();
        }

        private FindReplaceDialog findAndReplaceDialog;

        private void btnSaveAndRun_Click(object sender, RoutedEventArgs e)
        {
            try
            {

                if (SavePythonScriptFile())
                {
                    if (OwnerItem != null)
                    {
                        var startTime = System.DateTime.Now;
                        var startTag = $"\r\n[Start From:{startTime.ToShortDateString()} {startTime.ToShortTimeString()}]==========\r\n";
                        var result = PythonScriptListItem.RunScript(OwnerItem.CallMode, OwnerItem.FullPath, false);
                        var endTime = System.DateTime.Now;
                        var endTag = $"\r\n[End At:{endTime.ToShortDateString()} {endTime.ToShortTimeString()}]----------\r\n";
                        if (result != null)
                        {
                            tbxOutput.AppendText(startTag);
                            var outputInfo = Utils.Text.FormatLineSplitter(result.OutputInfo);
                            tbxOutput.AppendText(outputInfo);
                            tbxOutput.AppendText(endTag);
                            tbxOutput.ScrollToEnd();

                            if (result.ErrInfo.Length > 0)
                            {
                                tbxErr.AppendText(startTag);
                                tbxErr.AppendText(Utils.Text.FormatLineSplitter(result.ErrInfo));
                                tbxErr.AppendText(endTag);
                                tbxErr.ScrollToEnd();

                                tcBottom.SelectedIndex = 1;
                            }
                            else
                            {
                                tcBottom.SelectedIndex = 0;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnSaveAndClose_Click(object sender, RoutedEventArgs e)
        {
            if (SavePythonScriptFile()) this.Close();
        }

        private void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (isLmePyScript) return;

            if (editorBase.IsModified)
            {
                var answer = LMessageBox.Show("编辑区的脚本已修改，要保存吗？", Globals.AppName,
                    MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, "", null, this);
                switch (answer)
                {
                    case MessageBoxResult.Yes:
                        {
                            SavePythonScriptFile();
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            break;
                        }
                    case MessageBoxResult.Cancel:
                        {
                            e.Cancel = true;
                            break;
                        }
                }
            }
        }

        private void miCut_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Cut();
        }

        private void miCopy_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Copy();
        }

        private void miPaste_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Paste();
        }

        private void miUndo_Click(object sender, RoutedEventArgs e)
        {
            if (editorBase.CanUndo) editorBase.Undo();
        }

        private void miRedo_Click(object sender, RoutedEventArgs e)
        {
            if (editorBase.CanRedo) editorBase.Redo();
        }

        private void miSelectAll_Click(object sender, RoutedEventArgs e)
        {
            editorBase.SelectAll();
        }

        private void MetroWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            // 这几个快捷键都是全窗口级别的。
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            switch (e.Key)
            {
                case Key.Escape:
                    {
                        if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.IsVisible)
                        {
                            this.editorBase.CompletionWindow.Hide();
                        }
                        e.Handled = true;
                        break;
                    }
                case Key.S:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            SavePythonScriptFile();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F1:
                    {
                        if (!isCtrl && !isShift && !isAlt)
                        {
                            var selText = this.editorBase.SelectedText;

                            if (string.IsNullOrEmpty(selText))
                            {
                                var lt = this.editorBase.LeftText;
                                var rt = this.editorBase.RightText;
                                var regex_left = new Regex(@"[一-龥A-Za-zａ-ｚＡ-Ｚ0-9０-９_]{1,}$");
                                var regex_right = new Regex(@"^[一-龥A-Za-zａ-ｚＡ-Ｚ0-9０-９_]{1,}");
                                var match_left = regex_left.Match(lt);
                                var match_right = regex_right.Match(rt);
                                var sel = this.editorBase.SelectionStart;
                                var sel_len = 0;

                                if (match_left.Success)
                                {
                                    sel -= match_left.Length;
                                    sel_len = match_left.Length;
                                }

                                if (match_right.Success)
                                {
                                    sel_len += match_right.Length;
                                }

                                if (sel_len > 0)
                                {
                                    this.editorBase.Select(sel, sel_len);
                                    selText = this.editorBase.SelectedText;
                                }
                            }

                            if (selText.Length > 0 && selText.Contains("\r\n") == false)
                            {
                                string helpFilePath = Globals.InstalledPath + "ironpython-for-lme.chm";

                                System.Windows.Forms.Help.ShowHelp(null, helpFilePath,
                                    System.Windows.Forms.HelpNavigator.KeywordIndex,
                                    this.editorBase.SelectedText);
                            }
                        }
                        break;
                    }
                case Key.F2:
                    {
                        if (!isAlt && !isShift && !isCtrl)
                        {
                            var selText = this.editorBase.SelectedText;

                            if (string.IsNullOrEmpty(selText))
                            {
                                var lt = this.editorBase.LeftText;
                                var rt = this.editorBase.RightText;
                                var regex_left = new Regex(@"[一-龥A-Za-zａ-ｚＡ-Ｚ0-9０-９_]{1,}$");
                                var regex_right = new Regex(@"^[一-龥A-Za-zａ-ｚＡ-Ｚ0-9０-９_]{1,}");
                                var match_left = regex_left.Match(lt);
                                var match_right = regex_right.Match(rt);
                                var sel = this.editorBase.SelectionStart;
                                var sel_len = 0;

                                if (match_left.Success)
                                {
                                    sel -= match_left.Length;
                                    sel_len = match_left.Length;
                                }

                                if (match_right.Success)
                                {
                                    sel_len += match_right.Length;
                                }

                                if (sel_len > 0)
                                {
                                    this.editorBase.Select(sel, sel_len);
                                    selText = this.editorBase.SelectedText;
                                }
                            }

                            // 这个判断不能去除
                            if (string.IsNullOrWhiteSpace(selText) == false)
                            {
                                ateArgsInfo.Text = "";

                                foreach (var item in this.editorBase.InnerCompletionData)
                                {
                                    if (item.Text == selText)
                                    {
                                        var obsMark = "";
                                        var citem = item as CustomCompletionData;
                                        if (citem != null && citem.IsObsoleting)
                                        {
                                            obsMark = "(废弃的)";
                                        }
                                        ateArgsInfo.Text += obsMark + "【" + selText + "】\r\n" + new string('-', 4 + Utils.Text.GetTextWidth(selText)) + "\r\n" + item.Description.ToString() + "\r\n\r\n===================\r\n\r\n";
                                        // return 只会显示一条信息，所以不能用 return;  // 不能用 break;
                                    }
                                }

                                if (string.IsNullOrEmpty(ateArgsInfo.Text))
                                {
                                    ateArgsInfo.Text = "[未找到信息...]";
                                }

                                tcBottom.SelectedIndex = 2;
                            }
                        }
                        break;
                    }
                case Key.F5:
                    {
                        //保存并执行
                        if (!isCtrl && !isShift && !isAlt)
                        {
                            btnSaveAndRun_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F6:
                    {
                        if (isCtrl && isShift && !isAlt)
                        {
                            if (SavePythonScriptFile())
                            {
                                this.Close();
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.H:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            Replace();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            Find();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D0:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miShowCodeArea_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D1:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miShowOutputInfo_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D2:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miResetLayout_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D3:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miShowErrorInfo_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.R:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miAutoWrap_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.Q:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miSwitchMainWindowCollapse_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.O:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            miGetFilePath_Click(sender, e);
                        }
                        break;
                    }
                case Key.D:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            // Ctrl + D 相当于 Shift + Delete
                            var line = this.EditorBase.Document.GetLineByOffset(this.EditorBase.SelectionStart);
                            if (line != null)
                            {
                                this.EditorBase.Select(line.Offset, 0);
                                this.EditorBase.Document.Replace(line.Offset, line.Length + line.DelimiterLength, "");
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.Delete:
                    {
                        if (!isCtrl && isShift && !isAlt)
                        {
                            // Shift + Delete
                            var line = this.EditorBase.Document.GetLineByOffset(this.EditorBase.SelectionStart);
                            if (line != null)
                            {
                                this.EditorBase.Select(line.Offset, 0);
                                this.EditorBase.Document.Replace(line.Offset, line.Length + line.DelimiterLength, "");
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.Z:
                    {
                        if (!isShift && isCtrl && isAlt)
                        {
                            miCollapseCurrentFolder_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.T:
                    {
                        if (isShift && isCtrl && !isAlt)
                        {
                            miFormatVars_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.OemPlus:
                    {
                        if (!isShift && !isAlt && !isCtrl)
                        {
                            // 注意：此功能只有在输入法处于 英文 状态时才有效——因为中文状态下，e.Key == Key.ImeProcessed，此时无法运行到这里。
                            var ops = new string[] { " =", " +", " -", " *", " /", " !", " %", " ^", " &", " |", " <", " >", };
                            var leftMark = "";
                            var leftTxt = this.editorBase.LeftText;
                            foreach (var op in ops)
                            {
                                if (leftTxt.EndsWith(op))
                                {
                                    leftMark = op;
                                    break;
                                }
                            }

                            if (string.IsNullOrEmpty(leftMark) == false)
                            {
                                this.editorBase.Document.Replace(this.editorBase.SelectionStart, 0, "= ");
                                if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.Visibility == Visibility.Visible)
                                {
                                    this.editorBase.CompletionWindow.Close();
                                }
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                    //case Key.OemQuestion:  // 这里抓 / 字符，两个 // 之后加个空格。
                    //    {
                    //        if (!isShift && !isAlt && !isCtrl)
                    //        {
                    //            var leftTxt = this.editorBase.LeftText;

                    //            if (leftTxt.EndsWith(" /"))
                    //            {
                    //                this.editorBase.Document.Replace(this.editorBase.SelectionStart, 0, "/ ");
                    //                if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.Visibility == Visibility.Visible)
                    //                {
                    //                    this.editorBase.CompletionWindow.Close();
                    //                }
                    //                e.Handled = true;
                    //            }
                    //        }
                    //        break;
                    //    }
            }
        }

        private void PythonEditor_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            // 当输入法处于中文状态时，PreviewKeyDown 事件得到的总是 Key.ImeProcessed，所以要在这里处理 = 号输入的情况。

            if (e.Text == "=")
            {
                // 这几个快捷键都是全窗口级别的。
                KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
                KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
                KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
                KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
                KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
                KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

                bool isCtrl, isShift, isAlt;

                isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
                isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
                isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

                if (isCtrl || isShift || isAlt) return;

                var leftTxt = this.editorBase.LeftText;

                if (leftTxt.EndsWith(" /"))
                {
                    this.editorBase.Document.Replace(this.editorBase.SelectionStart, 0, "/ ");
                    if (this.editorBase.CompletionWindow != null && this.editorBase.CompletionWindow.Visibility == Visibility.Visible)
                    {
                        this.editorBase.CompletionWindow.Close();
                    }
                    e.Handled = true;
                }
            }
        }

        private void Replace()
        {
            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.tabMain.SelectedIndex = 1;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            if (editorBase.IsReadOnly)
            {
                findAndReplaceDialog.IsReplacePanelVisible = false;

                if (!editorBase.TextArea.Selection.IsMultiline)
                {
                    findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind.Focus();
                }
            }
            else
            {
                findAndReplaceDialog.IsReplacePanelVisible = true;

                if (!editorBase.TextArea.Selection.IsMultiline)
                {
                    findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                    findAndReplaceDialog.txtFind.SelectAll();
                    findAndReplaceDialog.txtFind2.SelectAll();
                    findAndReplaceDialog.txtFind2.Focus();
                }
            }

        }

        private void Find()
        {
            findAndReplaceDialog.Owner = this;
            findAndReplaceDialog.tabMain.SelectedIndex = 0;
            findAndReplaceDialog.Show();
            findAndReplaceDialog.Activate();

            if (!editorBase.TextArea.Selection.IsMultiline)
            {
                findAndReplaceDialog.txtFind.Text = findAndReplaceDialog.txtFind2.Text = editorBase.TextArea.Selection.GetText();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.SelectAll();
                findAndReplaceDialog.txtFind.Focus();
            }

            if (editorBase.IsReadOnly)
            {
                findAndReplaceDialog.IsReplacePanelVisible = false;
            }
            else findAndReplaceDialog.IsReplacePanelVisible = true;
        }

        private void miFindMain_Click(object sender, RoutedEventArgs e)
        {
            Find();
        }

        private void miReplaceMain_Click(object sender, RoutedEventArgs e)
        {
            Replace();
        }

        private void miAutoWrap_Click(object sender, RoutedEventArgs e)
        {
            bool wrap =
                editorBase.WordWrap = (miAutoWrap.IsChecked = !miAutoWrap.IsChecked);

            if (wrap)
            {
                tbxOutput.TextWrapping = TextWrapping.Wrap;
                tbxErr.TextWrapping = TextWrapping.Wrap;
            }
            else
            {
                tbxOutput.TextWrapping = TextWrapping.NoWrap;
                tbxErr.TextWrapping = TextWrapping.NoWrap;
            }
        }

        private void btnTopmost_Click(object sender, RoutedEventArgs e)
        {
            this.Topmost = !this.Topmost;
            if (this.Topmost)
            {
                backgroundGridTopMost.Background = Brushes.DarkCyan;
            }
            else
            {
                backgroundGridTopMost.Background = Brushes.Transparent;
            }
        }

        private void miShowOutputInfo_Click(object sender, RoutedEventArgs e)
        {
            rdOutputArea.Height = new GridLength(3, GridUnitType.Star);
            rdMainEditArea.Height = new GridLength(1, GridUnitType.Star);
            cdLeft.Width = new GridLength(4, GridUnitType.Star);
            cdRight.Width = new GridLength(1, GridUnitType.Star);
        }

        private void miShowErrorInfo_Click(object sender, RoutedEventArgs e)
        {
            rdOutputArea.Height = new GridLength(3, GridUnitType.Star);
            rdMainEditArea.Height = new GridLength(1, GridUnitType.Star);
            cdLeft.Width = new GridLength(1, GridUnitType.Star);
            cdRight.Width = new GridLength(4, GridUnitType.Star);
        }

        private void miResetLayout_Click(object sender, RoutedEventArgs e)
        {
            rdMainEditArea.Height = new GridLength(3, GridUnitType.Star);
            rdOutputArea.Height = new GridLength(1, GridUnitType.Star);
            cdLeft.Width = new GridLength(1, GridUnitType.Star);
            cdRight.Width = new GridLength(1, GridUnitType.Star);
        }

        private void miShowCodeArea_Click(object sender, RoutedEventArgs e)
        {
            rdMainEditArea.Height = new GridLength(1, GridUnitType.Star);
            rdOutputArea.Height = new GridLength(0, GridUnitType.Star);
        }

        private void MetroWindow_Closed(object sender, EventArgs e)
        {
            if (this.MasterItem != null)
                MasterItem.Editor = null;
        }

        /// <summary>
        /// 解决 Win10 下菜单位置偏移的问题。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Menu_Initialized(object sender, EventArgs e)
        {
            var m = sender as MenuItem;
            if (m == null) return;
            var cm = m.ContextMenu;
            if (cm == null) return;

            cm.Placement = PlacementMode.Bottom;
            cm.PlacementTarget = m;
            m.ContextMenu = null;
            m.Tag = cm;

            cm.Opened += Cm_Opened;
            cm.Closed += Cm_Closed;
            m.PreviewMouseLeftButtonDown += M_PreviewMouseLeftButtonDown;
        }

        private void M_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var m = sender as MenuItem;
            if (m == null) return;
            var cm = m.Tag as ContextMenu;
            if (cm == null) return;

            cm.IsOpen = true;
        }

        private void Cm_Closed(object sender, RoutedEventArgs e)
        {
            var cm = sender as ContextMenu;
            if (cm == null || cm.PlacementTarget == null) return;
            var m = cm.PlacementTarget as MenuItem;
            if (m == null) return;

            m.Background = Brushes.Transparent;
            m.Foreground = Brushes.White;
        }

        private void Cm_Opened(object sender, RoutedEventArgs e)
        {
            var cm = sender as ContextMenu;
            if (cm == null || cm.PlacementTarget == null) return;
            var m = cm.PlacementTarget as MenuItem;
            if (m == null) return;

            m.Background = Brushes.LightGray;
            m.Foreground = Brushes.Black;

            var point = cm.PlacementTarget.PointToScreen(new Point(0, 0));
            //if (Environment.OSVersion.Version.Major > 6 ||
            //    (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1))  // win 8 以上（6.2）
            //{
            cm.HorizontalOffset = 0;  // -cm.ActualWidth + m.ActualWidth + 6;// point.X - splitMenu2.ActualWidth + btnFindHeaders_AppendArrow.ActualWidth;
            cm.VerticalOffset = 0;  // point.Y - 8;
            //}
        }

        private void Menu_Click(object sender, RoutedEventArgs e)
        {
            var menu = sender as MenuItem;
            if (menu == null || menu.Tag == null) return;
            var cm = menu.Tag as ContextMenu;
            if (cm == null) return;

            cm.IsOpen = true;
        }

        private void miShowCompleteWindow_Click(object sender, RoutedEventArgs e)
        {
            this.editorBase.ShowCompletionWindowManual(e);
        }

        private void btnUndo_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Undo();
        }

        private void btnRedo_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Redo();
        }

        private void btnCut_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Cut();
        }

        private void btnCopy_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Copy();
        }

        private void btnPaste_Click(object sender, RoutedEventArgs e)
        {
            editorBase.Paste();
        }

        private void miEnableAutoComplete_Click(object sender, RoutedEventArgs e)
        {
            editorBase.EnableAutoComplete = !editorBase.EnableAutoComplete;
        }

        private void spEnableAutoComplete_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            miEnableAutoComplete_Click(sender, e);
        }

        private void miCommentSelectedLines_Click(object sender, RoutedEventArgs e)
        {
            editorBase.CommentSelectedLines();
        }

        private void miShowSpaces_Click(object sender, RoutedEventArgs e)
        {
            miShowSpaces.IsChecked =
                editorBase.Options.ShowSpaces = !editorBase.Options.ShowSpaces;
        }

        private void miShowEndOfLine_Click(object sender, RoutedEventArgs e)
        {
            miShowEndOfLine.IsChecked =
                editorBase.Options.ShowEndOfLine = !editorBase.Options.ShowEndOfLine;
        }

        private void miShowTabs_Click(object sender, RoutedEventArgs e)
        {
            miShowTabs.IsChecked =
                editorBase.Options.ShowTabs = !editorBase.Options.ShowTabs;
        }

        private void miShowLineNumbers_Click(object sender, RoutedEventArgs e)
        {
            miShowLineNumbers.IsChecked =
                editorBase.ShowLineNumbers = !editorBase.ShowLineNumbers;
        }

        private void miHelp_Click(object sender, RoutedEventArgs e)
        {
            string helpFilePath = Globals.InstalledPath + "ironpython-for-lme.chm";

            if (string.IsNullOrEmpty(this.editorBase.SelectedText))
            {
                System.Windows.Forms.Help.ShowHelp(null, helpFilePath);
            }
            else
            {
                System.Windows.Forms.Help.ShowHelp(null, helpFilePath,
                    System.Windows.Forms.HelpNavigator.KeywordIndex,
                    this.editorBase.SelectedText);
            }
        }

        private void miLesson_Click(object sender, RoutedEventArgs e)
        {
            string helpFilePath = Globals.InstalledPath + "LME脚本教程.chm";

            if (string.IsNullOrEmpty(this.editorBase.SelectedText))
            {
                System.Windows.Forms.Help.ShowHelp(null, helpFilePath);
            }
            else
            {
                System.Windows.Forms.Help.ShowHelp(null, helpFilePath,
                    System.Windows.Forms.HelpNavigator.KeywordIndex,
                    this.editorBase.SelectedText);
            }
        }

        private void miFindCompleteTip_Click(object sender, RoutedEventArgs e)
        {
            var selText = this.editorBase.SelectedText;
            if (string.IsNullOrWhiteSpace(selText) == false)
            {
                foreach (var item in this.editorBase.InnerCompletionData)
                {
                    if (item.Text == selText)
                    {
                        var obsMark = "";
                        var citem = item as CustomCompletionData;
                        if (citem != null && citem.IsObsoleting)
                        {
                            obsMark = "(废弃的)";
                        }
                        ateArgsInfo.Text = obsMark + "【" + selText + "】" + "\r\n" + new string('=', Utils.Text.GetTextWidth(selText)) + "\r\n" + item.Description.ToString();
                        tcBottom.SelectedIndex = 2;
                        return;  // 不能用 break;
                    }
                }
                ateArgsInfo.Text = "[未找到信息...]";
                tcBottom.SelectedIndex = 2;
            }
        }

        private void miSwitchMainWindowCollapse_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.SwitchOnlyShowTitleBar();
        }

        private void miGetFilePath_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == true)
            {
                var leftText = this.editorBase.LeftText;
                var rightText = this.editorBase.RightText;

                if ((leftText.EndsWith("\"") && rightText.StartsWith("\"")) ||
                    (leftText.EndsWith("\'") && rightText.EndsWith("\'")))
                {
                    if (leftText.Length >= 2 && (leftText[leftText.Length - 2] == 'r' || leftText[leftText.Length - 2] == 'R'))
                    {
                        this.editorBase.SelectedText = ofd.FileName;
                        var length = this.editorBase.SelectedText.Length;
                        var lastIndex = this.editorBase.SelectedText.LastIndexOf('\\');
                        if (lastIndex >= 0)
                        {
                            this.editorBase.Select(this.editorBase.SelectionStart + lastIndex, length - lastIndex);
                        }
                        else
                        {
                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectedText.Length, 0);
                        }
                    }
                    else
                    {
                        var formatPath = ofd.FileName.Replace("\\", "\\\\");
                        this.editorBase.SelectedText = formatPath;

                        var length = formatPath.Length;
                        var lastIndex = formatPath.LastIndexOf("\\\\");
                        if (lastIndex >= 0)
                        {
                            this.editorBase.Select(this.editorBase.SelectionStart + lastIndex, length - lastIndex);
                        }
                        else
                        {
                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectedText.Length, 0);
                        }
                    }
                }
                else
                {
                    var formatPath = "r\'" + ofd.FileName + "\'";
                    this.editorBase.SelectedText = formatPath;

                    var length = formatPath.Length;
                    var lastIndex = formatPath.LastIndexOf("\\");
                    if (lastIndex >= 0)
                    {
                        this.editorBase.Select(this.editorBase.SelectionStart + lastIndex, length - lastIndex - 1);
                    }
                    else
                    {
                        this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectedText.Length, 0);
                    }
                }

                e.Handled = true;
            }
        }

        private void bdAutoWrap_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (tbxErr.HorizontalScrollBarVisibility != ScrollBarVisibility.Visible)
            {
                tbxErr.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
                tbxOutput.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
                ateArgsInfo.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
                tbxErr.TextWrapping = TextWrapping.NoWrap;
                tbxOutput.TextWrapping = TextWrapping.NoWrap;
                ateArgsInfo.WordWrap = false;

                bdAutoWrap.Background = Brushes.Transparent;
            }
            else if (tbxErr.HorizontalScrollBarVisibility == ScrollBarVisibility.Visible)
            {
                tbxErr.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                tbxOutput.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                ateArgsInfo.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
                tbxErr.TextWrapping = TextWrapping.Wrap;
                tbxOutput.TextWrapping = TextWrapping.Wrap;
                ateArgsInfo.WordWrap = true;

                bdAutoWrap.Background = Brushes.White;
            }
        }

        private void miCollapseCurrentFolder_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            this.EditorBase.FoldSelectedBlock();
        }

        private void miCollapseAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }
        }

        private void miExpandAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }
        }

        private void miInsertBackgroundTaskCodeSample_Click(object sender, RoutedEventArgs e)
        {
            if (this.editorBase == null) return;

            this.editorBase.SelectedText = @"
# **** 后台任务处理-Start ****

# [注意]后台任务调试困难，难以捕获异常，请尽量写一点就测试一点。

def 我的任务(任务参数, 信息参数):
    # 先设置要执行的任务数量
    任务参数.置任务数(15)
    # 要执行的任务写在此处
    # 【注意】尽量不要调用外部方法，会抛出“不能访问已关闭的流”异常
    for i in range(15):
        # 不能在这个函数中直接更新主界面（因为不在同一线程）
        任务参数.报告任务进度(i, '进度：' + str(i))

def 任务完成后(任务参数, 信息参数):
    # 这里用来提示任务已完成，也可以弹出一个消息框。
    任务参数.置任务输出信息行(""已完成："" + 任务参数.任务名称 + ""。"")

def 任务取消报告(任务参数, 信息参数):
    # 这里用来提示任务已被取消，也可以弹出一个消息框。
    任务参数.置任务输出信息行(""已取消："" + 任务参数.任务名称 + ""。"")

def 任务进度报告(任务参数, 信息参数):
    # 在这里更新任务进度（这里回到主线程了）
    任务参数.置任务输出信息行(信息参数.进度)

# 初始化后台任务（默认报告进度并支持取消）
新任务 = 后台任务(""A sample Task"")

# “挂接”几个事件处理器函数
新任务.任务执行函数 += 我的任务
新任务.任务完成函数 += 任务完成后
新任务.任务取消函数 += 任务取消报告
新任务.任务进度报告函数 += 任务进度报告

# 启动任务
新任务.开始任务()

# [注]任务不需要通过代码取消。

# **** 后台任务处理-End ****
";
        }

        private void miFormatVars_Click(object sender, RoutedEventArgs e)
        {
            if (this.editorBase == null) return;

            // 根据 当前行 向 前方、后方 分别查找赋值行并进行格式化。
            var regText = @"(?<=(^[ \t]*))[_\u4e00-\u9fffA-Za-z][_\u4e00-\u9fffA-Za-z0-9]*[ 　\t]{0,}([\+\-\*\/\%\&\^\|]|\*\*|//|<<|>>)?(?=([=]))";
            var curLine = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            if (curLine == null) return;
            var regEx = new Regex(regText);
            var curLineText = this.editorBase.Document.GetText(curLine.Offset, curLine.Length);
            var curMatch = regEx.Match(curLineText);
            if (curMatch.Success == false) return;

            var preLines = this.editorBase.GetPreviewAsignLines(curLine.LineNumber, regEx);
            var nextLines = this.editorBase.GetNextAsignLines(curLine.LineNumber, regEx);

            if (preLines == null && nextLines == null) return;

            var list = new List<VarInfo>();
            if (preLines != null)
            {
                list.AddRange(preLines);
            }

            var doubleMarks = new string[] { "<<", ">>", "//", "**", };
            var singleMarks = new string[] { "+", "-", "*", "/", "%", "&", "|", "^", };
            var curAsignMark = curMatch.Value.Substring(curMatch.Length - 2);
            if (doubleMarks.Contains(curAsignMark))
            {
                list.Add(new VarInfo(curMatch, curLine, curAsignMark));
            }
            else
            {
                curAsignMark = curMatch.Value.Substring(curMatch.Length - 1);
                if (singleMarks.Contains(curAsignMark))
                {
                    list.Add(new VarInfo(curMatch, curLine, curAsignMark));
                }
                else
                {
                    list.Add(new VarInfo(curMatch, curLine, ""));
                }
            }

            if (nextLines != null)
            {
                list.AddRange(nextLines);
            }

            var trimChars = new char[] { ' ', '　', '\t', '+', '-', '*', '/', '&', '^', '<', '>', '|' };
            var maxVarWidth = 0;
            var maxAsignMarkWidth = 0;

            foreach (var varInfo in list)
            {
                maxVarWidth = Math.Max(maxVarWidth, Utils.Text.GetTextWidth(varInfo.VarMath.Value.TrimEnd(trimChars)));
                maxAsignMarkWidth = Math.Max(maxAsignMarkWidth, varInfo.AssignPrefix.Length);
            }

            if (maxAsignMarkWidth > 2) maxAsignMarkWidth = 2; // 不可能出现大于二的情况

            this.editorBase.BeginChange();
            for (int i = list.Count - 1; i >= 0; i--)
            {
                var vi = list[i];
                var varName = vi.VarMath.Value.TrimEnd(trimChars);
                var varValueOffset = vi.DocumentLine.Offset + vi.VarMath.Index + vi.VarMath.Length;
                var varValue = this.editorBase.Document.GetText(varValueOffset, vi.DocumentLine.EndOffset - varValueOffset).TrimStart(new char[] { ' ', '　', '\t', '=', '+', '-', '*', '/', '&', '^', '<', '>', '|' });
                var replaceStartOfset = vi.DocumentLine.Offset + vi.VarMath.Index;
                if (vi.AssignPrefix.Length <= 0)
                {
                    this.editorBase.Document.Replace(replaceStartOfset, vi.DocumentLine.EndOffset - replaceStartOfset, varName + new string(' ', maxVarWidth - varName.Length + 1 + maxAsignMarkWidth) + vi.AssignPrefix + "= " + varValue);
                }
                else if (vi.AssignPrefix.Length == 1)
                {
                    this.editorBase.Document.Replace(replaceStartOfset, vi.DocumentLine.EndOffset - replaceStartOfset, varName + new string(' ', maxVarWidth - varName.Length + maxAsignMarkWidth) + vi.AssignPrefix + "= " + varValue);
                }
                else
                {
                    this.editorBase.Document.Replace(replaceStartOfset, vi.DocumentLine.EndOffset - replaceStartOfset, varName + new string(' ', maxVarWidth - varName.Length + maxAsignMarkWidth - 1) + vi.AssignPrefix + "= " + varValue);
                }
            }
            this.editorBase.EndChange();

            var newCurLine = this.editorBase.Document.GetLineByNumber(curLine.LineNumber);
            var newCurLineText = this.editorBase.Document.GetText(newCurLine.Offset, newCurLine.Length);
            var newRegex = new Regex(@"(?<=(^[ \t]*))[_\u4e00-\u9fffA-Za-z][_\u4e00-\u9fffA-Za-z0-9]*[ 　\t]{0,}[=][ ]*");
            var newMatch = newRegex.Match(newCurLineText);
            if (newMatch.Success)
            {
                this.editorBase.Select(newCurLine.Offset + newMatch.Length, 0);
            }
        }

        private void miPasteRegionText_Click(object sender, RoutedEventArgs e)
        {
            this.EditorBase?.PasteRegionalText();
        }

        private void btnOpenWorkspaceAssetsFolder_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var path = Globals.PathOfWorkspaceAssetsFolder;
                if (Directory.Exists(path) == false)
                {
                    Directory.CreateDirectory(path);
                    System.IO.File.WriteAllText(path + "资源文件夹说明.txt", "此目录用以存放不会被导出的一些附属资源文件——例如一些 Word 文档等。", Encoding.UTF8);
                }

                System.Diagnostics.Process.Start("explorer.exe", $"\"{path}\"");
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void btnOpenOutPutFolder_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var path = Globals.PathOfWorkspaceOutputDocsFolder;
                if (Directory.Exists(path) == false)
                {
                    Directory.CreateDirectory(path);
                }

                System.Diagnostics.Process.Start("explorer.exe", $"\"{path}\"");
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private void MetroWindow_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                // Get the array of files that were dropped.
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                // Assume you only want the first file in the list.
                if (files.Length <= 0) return;

                var sb = new StringBuilder();

                var leftTxt = this.EditorBase.LeftText;
                var rightTxt = this.EditorBase.RightText;

                if ((leftTxt.EndsWith("\'") && rightTxt.StartsWith("\'")) ||
                    (leftTxt.EndsWith("\"") && rightTxt.StartsWith("\"")))
                {
                    if (leftTxt.EndsWith("r\'") || leftTxt.EndsWith("R\'") ||
                        leftTxt.EndsWith("r\"") || leftTxt.EndsWith("R\""))
                    {
                        foreach (string file in files)
                        {
                            sb.Append(file + "\r\n");
                        }
                    }
                    else
                    {
                        foreach (string file in files)
                        {
                            sb.Append(file.Replace("\\", "\\\\") + "\r\n");
                        }
                    }
                }
                else
                {
                    foreach (string file in files)
                    {
                        sb.Append("r\'" + file + "\' \\\r\n");
                    }
                }

                this.EditorBase.SelectedText = sb.ToString().TrimEnd(new char[] { '\r', '\n', ' ', '\\' });
            }
        }
    }
}
